#include "Task.hpp"
#include <string>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <assert.h>
#include <ctime>
#include <sys/wait.h>
#include <sys/stat.h>
const int processNum = 5; // 创建子进程的个数
std::vector<task_t> tasks;

// 描述管道
class channel
{
public:
    channel(int cmdfd, int slaverid, const std::string &processname)
        : _cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
    {
    }

public:
    int _cmdfd;               // 发送任务的文件描述符
    pid_t _slaverid;          // 子进程的PID
    std::string _processname; // 子进程的名字 --- 方便打印日志
};

void slaver()
{
    // read(0) --- 从键盘上读取任务
    while (true)
    {
        int cmdcode = 0; // 任务码
        int n = read(0, &cmdcode, sizeof(int));
        if (n == sizeof(int))
        {
            // 执行cmdcode对应的任务列表
            std::cout << "slaver say@ get a command: " << getpid() << " : cmdcode: " << cmdcode << std::endl;
            if (cmdcode >= 0 && cmdcode < tasks.size())
            {
                tasks[cmdcode]();
            }
        }
        else if (n == 0)
        {
            break;
        }
    }
}

/*
传参方式讲解
1. 输入型参数：用于传递数据给函数，但形参的改变不用影响实参 -> const&
2. 输出型参数：形参改变要影响实参，当函数被调用时，输出型参数可以不初始化 -> *
3. 输入输出型参数：和输出型参数的区别是 -> 这些参数在函数调用前需要被初始化 -> &
*/
void InitProcess(std::vector<channel> *channels)
{
    // 有多少个进程有有多少个管道
    for (int i = 0; i < processNum; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd); // 建立管道
        assert(!n);           // n=0表示创建管道成功，失败返回-1
        (void)n;              // 如果一个变量被标记为 (void)，意味着在这个上下文中，程序员明确地表明不打算使用这个变量的值。

        pid_t id = fork(); // bash创建子进程
        if (id == 0)       // 子进程
        {
            // 子进程负责读管道的数据（父进程派发的任务）
            close(pipefd[1]); // 对应就要关闭写端
            // 接下来子进程要执行任务
            dup2(pipefd[0], 0); // 原本要从管道文件读改为向键盘读
            slaver();           // 子进程需要完成的任务
            std::cout << "process: " << getpid() << " quit" << std::endl;
            exit(0);
        }
        else // 父进程
        {
            // 父进程负责写入数据到管道给子进程
            close(pipefd[0]); // 对应就要关闭读端
            // 添加channel字段
            std::string name = "process" + std::to_string(i);
            channels->push_back(channel(pipefd[1], id, name));
        }
    }
}

void Debug(const std::vector<channel> &channels)
{
    for (const auto &item : channels)
    {
        std::cout << item._cmdfd << " " << item._slaverid << " " << item._processname << std::endl;
    }
}

// 随机选取进程
void ctrlsalver1(const std::vector<channel> &channels)
{
    for (int i = 1; i <= 100; i++)
    {
        // 1. 选择任务
        // int cmdcode = rand() % 20;
        int cmdcode = rand() % tasks.size();
        // 2. 选择进程
        int processPos = rand() % channels.size(); // 生成[0, channels.size() - 1]之间的随机下标，使得每个进程都能享有任务，实现负载均衡
        std::cout << "father say: " << "cmdcode: " << cmdcode << " already send to " << channels[processPos]._slaverid << " processname: " << channels[processPos]._processname << std::endl;
        // 3. 发送任务
        write(channels[processPos]._cmdfd, &cmdcode, sizeof(cmdcode));
        sleep(1);
    }
}

// 轮询选取进程
void ctrlsalver2(const std::vector<channel> &channels)
{
    int which = 0;
    int cnt = 5;
    while (cnt)
    {
        // 1. 选择任务
        // int cmdcode = rand() % 20;
        int cmdcode = rand() % tasks.size();
        // 2. 选择进程
        std::cout << "father say: " << "cmdcode: " << cmdcode << " already send to " << channels[which]._slaverid << " processname: " << channels[which]._processname << std::endl;
        // 3. 发送任务
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));

        which++;
        which %= channels.size();
        cnt--;
        sleep(1);
    }
}

void Menu()
{
    std::cout << "##################################" << std::endl;
    std::cout << "##### 1. 刷新日志 2. 更新野区 #####" << std::endl;
    std::cout << "##### 3. 检测更新 4. 用户释放技能 #####" << std::endl;
    std::cout << "#####        0. 退出         #####" << std::endl;
    std::cout << "##################################" << std::endl;
}

// 手动控制执行任务
void ctrlsalver3(const std::vector<channel> &channels)
{
    int which = 0;
    while (true)
    {
        int select = 0;
        Menu();
        std::cout << "请输入:";
        std::cin >> select;
        if (select == 0)
            break;
        else if (select <= 0 || select >= 5)
            break;

        // 后面就是符合任务下标了

        // 1. 选择任务
        int cmdcode = select - 1;
        // 2. 选择进程
        std::cout << "father say: " << "cmdcode: " << cmdcode << " already send to " << channels[which]._slaverid << " processname: " << channels[which]._processname << std::endl;
        // 3. 发送任务
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));

        which++;
        which %= channels.size();
        sleep(1);
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    // 关掉文件描述符
    for (const auto &c : channels)
    {
        close(c._cmdfd);
    }

    // sleep(10);

    // 父进程回收子进程
    for (const auto &c : channels)
    {
        waitpid(c._cmdfd, nullptr, 0);
    }
    // sleep(5);
}

int main()
{
    LoadTasks(&tasks);
    srand(time(nullptr) ^ getpid() ^ 1023); // 种一颗随机数种子

    // 再组织管道
    std::vector<channel> channels;
    // 1. 初始化
    InitProcess(&channels);

    // 测试一波
    Debug(channels);

    // sleep(100);
    //  2. 控制子进程 -- 具体查看slaver函数
    ctrlsalver3(channels);
    // 3. 清理收尾
    QuitProcess(channels);
    return 0;
}